﻿using System.Threading.Tasks;
using System.Collections.Generic;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.IO;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Net;
using UMC.Data;
using System.Buffers;
using System.Threading;
using System.IO.MemoryMappedFiles;

namespace UMC.Net
{
    public class NetPool
    {


        public NetPool(string key)
        {
            // MemoryMappedFile.CreateFromFile
            this.Key = key;
        }

        ConcurrentStack<NetProxy> _pool = new ConcurrentStack<NetProxy>();
        public ConcurrentStack<NetProxy> Unwanted
        {
            get
            {
                return _pool;
            }
        }
        public SemaphoreSlim Semaphore
        {
            get;
            private set;
        }
        /// <summary>
        /// 连接错误
        /// </summary>
        public int BurstError
        {
            get;
            set;
        }
        public String Key
        {
            get;
            private set;
        }
        public uint ErrorTime
        {
            get;
            set;
        }
        public int Qps
        {
            get;
            private set;
        }
        public void MaxQps(int qps)
        {
            if (qps > 0)
            {
                this.Qps = qps;
                this.Semaphore = new SemaphoreSlim(qps, qps);
            }
            else
            {
                this.Qps = 0;
                this.Semaphore = null;
            }
        }
    }

    /// <summary>
    /// HTTP请求代码与HttpClient机制增加了端口复用
    /// </summary>
    public abstract class NetProxy
    {
        public enum DNSType
        {
            Unknown,
            IP4 = 1,
            IP6 = 2,
            DNS = 4

        }
        public static void QPS(Uri uri, int qps)
        {
            Pool.GetOrAdd(uri.Authority, k => new NetPool(uri.Authority)).MaxQps(qps);
        }
        public static void KeepAlives(Uri uri, int timeout)
        {
            var key = uri.Authority;

            if (_pool.TryGetValue(key, out var _t))
            {
                while (_t.Unwanted.TryPop(out var tc))
                {
                    tc.Dispose();
                }
            }
            _KeepAlives[key] = timeout;
        }

        public bool IsError = false;

        public SemaphoreSlim Semaphore
        {
            get;
            private set;
        }
        protected void Release()
        {
            this.Semaphore?.Release();
            this.Semaphore = null;
        }
        public virtual void Log(NetWriteData writeData)
        {

        }

        protected Exception Error;
        public static void DNS(string host, bool isclear)
        {
            if (isclear)
            {
                _dns.TryRemove(host, out var _);
            }
            else
            {
                DNS(host);
            }
        }
        public static void DNS(string host, params IPAddress[] addresses)
        {
            if (_dns.TryGetValue(host, out var dns))
            {
                _dns[host] = Tuple.Create(addresses.Length > 0 ? uint.MaxValue : Utility.TimeSpan(), dns.Item2, addresses, 600);
            }
            else
            {
                _dns[host] = Tuple.Create(addresses.Length > 0 ? uint.MaxValue : Utility.TimeSpan(), DNSType.Unknown, addresses, 600);
            }
        }
        public static void DNS(string host, DNSType addressFamily)
        {

            _dns[host] = Tuple.Create(Utility.TimeSpan(), addressFamily, new IPAddress[0], 600);
        }
        public static void DNS(string host, DNSType addressFamily, params IPAddress[] addresses)
        {
            if (_dns.TryGetValue(host, out var dns))
            {

                dns = Tuple.Create(Utility.TimeSpan(), dns.Item2 | addressFamily, addresses, 600);
            }
            else
            {
                dns = Tuple.Create(Utility.TimeSpan(), addressFamily, addresses, 600);

            }
            _dns[host] = dns;
        }
        public Action<NetProxy> ErrorCall
        {
            get; set;
        }
        public static Dictionary<string, System.Tuple<uint, DNSType, IPAddress[], int>> DNS()
        {
            return new Dictionary<string, System.Tuple<uint, DNSType, IPAddress[], int>>(_dns);
        }
        // public static String AccessKeySecret
        // {
        //     get; set;
        // }
        // public static String AccessKeyID
        // {
        //     get; set;
        // }
        // public static String AccountID
        // {
        //     get; set;
        // }
        // public static async Task<(IPAddress[], int)> HttpDnsIPAsync(string host, DNSType family = DNSType.Unknown, CancellationToken cancellationToken = default)
        // {
        //     var ttl = 600;
        //     // if (String.IsNullOrEmpty(AccessKeySecret) || family.HasFlag(DNSType.DNS) == false)
        //     // {
        //     return (await Dns.GetHostAddressesAsync(host, cancellationToken), ttl);

        // }
        // var ts = Utility.TimeSpan();

        // var key = Utility.SHA256($"{AccountID}{AccessKeySecret}{ts}{host}{AccessKeyID}");
        // var Stype = family.HasFlag(DNSType.IP6) ? "AAAA" : "A";
        // var mime = await new Uri($"https://223.5.5.5/resolve?name={host}&type={Stype}&uid={AccountID}&ak={AccessKeyID}&key={key}&ts={ts}")
        // .WebRequest().GetAsync(cancellationToken);///.G(out var xhr);
        // var json = mime.Memory.Temp();

        // var list = new List<IPAddress>();
        // // var span = json.Span;// json.AsSpan();

        // var ttlIndex = json.Span.IndexOf("\"TTL\":"u8) + 6;
        // while (ttlIndex > 6)
        // {

        //     var ipsIndex = json.Span.IndexOf((byte)',', ttlIndex);
        //     Utility.TryParse(json.Slice(ttlIndex, ipsIndex - ttlIndex).Span, 10, out ttl);
        //     var typeIndex = json.Span.IndexOf("\"type\":"u8, false, ipsIndex) + 7;

        //     ipsIndex = json.Span.IndexOf((byte)',', typeIndex);

        //     Utility.TryParse(json.Span.Slice(typeIndex, typeIndex - ttlIndex), 10, out var type);
        //     switch (type)
        //     {
        //         case 1:
        //         case 28:
        //             var dataIndex = json.Span.IndexOf("\"data\":\""u8, false, ipsIndex) + 8;

        //             var dataEnd = json.Span.IndexOf((byte)'"', dataIndex);

        //             if (IPAddress.TryParse(json.Span.Slice(dataIndex, dataEnd - dataIndex).UTF8(), out var address))
        //             {
        //                 list.Add(address);
        //             }
        //             break;
        //     }
        //     ttlIndex = json.Span.IndexOf("\"TTL\":"u8, false, ttlIndex) + 6;
        // }
        // if (list.Count == 0)
        // {
        //     return (await Dns.GetHostAddressesAsync(host, cancellationToken), ttl);
        // }
        // return (list.ToArray(), ttl);// new IPAddress[0];
        // }

        // public static IPAddress[] HttpDnsIP(string host, out int ttl, DNSType family = DNSType.Unknown)
        // {
        //     ttl = 600;
        //     if (String.IsNullOrEmpty(AccessKeySecret) || family.HasFlag(DNSType.DNS) == false)
        //     {
        //         return Dns.GetHostAddresses(host);

        //     }
        //     var ts = Utility.TimeSpan();

        //     var key = Utility.SHA256($"{AccountID}{AccessKeySecret}{ts}{host}{AccessKeyID}");
        //     var Stype = family.HasFlag(DNSType.IP6) ? "AAAA" : "A";
        //     var json = new Uri($"https://223.5.5.5/resolve?name={host}&type={Stype}&uid={AccountID}&ak={AccessKeyID}&key={key}&ts={ts}")
        //     .WebRequest().GetBytes(out var xhr);


        //     var list = new List<IPAddress>();
        //     var span = json.AsSpan();

        //     var ttlIndex = span.IndexOf("\"TTL\":"u8) + 6;
        //     while (ttlIndex > 6)
        //     {

        //         var ipsIndex = Array.IndexOf(json, (byte)',', ttlIndex);
        //         Utility.TryParse(span.Slice(ttlIndex, ipsIndex - ttlIndex), 10, out ttl);
        //         var typeIndex = span.IndexOf("\"type\":"u8, false, ipsIndex) + 7;

        //         ipsIndex = Array.IndexOf(json, (byte)',', typeIndex);

        //         Utility.TryParse(span.Slice(typeIndex, typeIndex - ttlIndex), 10, out var type);
        //         switch (type)
        //         {
        //             case 1:
        //             case 28:
        //                 var dataIndex = span.IndexOf("\"data\":\""u8, false, ipsIndex) + 8;

        //                 var dataEnd = Array.IndexOf(json, (byte)'"', dataIndex);

        //                 if (IPAddress.TryParse(span.Slice(dataIndex, dataEnd - dataIndex).UTF8(), out var address))
        //                 {
        //                     list.Add(address);
        //                 }
        //                 break;
        //         }
        //         ttlIndex = span.IndexOf("\"TTL\":"u8, false, ttlIndex) + 6;
        //     }
        //     if (list.Count == 0)
        //     {
        //         return Dns.GetHostAddresses(host);
        //     }
        //     return list.ToArray();// new IPAddress[0];
        // }
        public static async Task<IPAddress[]> DNSToIPAsync(string host, CancellationToken cancellationToken = default)
        {
            if (IPAddress.TryParse(host, out var address))
            {
                return new IPAddress[1] { address };
            }
            var time = UMC.Data.Utility.TimeSpan();
            if (_dns.TryGetValue(host, out var dns) == false)
            {
                var hindex = host.IndexOf('.');
                while (hindex > 0)
                {
                    if (_dns.TryGetValue("*" + host.Substring(hindex), out dns))
                    {
                        break;
                    }
                    hindex = host.IndexOf('.', hindex + 1);

                }
                if (dns == null)
                {
                    var ips = await Dns.GetHostAddressesAsync(host, cancellationToken);// HttpDnsIP(host, out var ttl);
                    dns = Tuple.Create(time, DNSType.Unknown, ips, 600);
                    _dns[host] = dns;
                    return ips;

                }
            }
            if (dns.Item3.Length == 0)
            {
                var ips = await Dns.GetHostAddressesAsync(host, cancellationToken);
                dns = Tuple.Create(time, dns.Item2, ips, 600);
                _dns[host] = dns;
            }
            else
            {

                if (dns.Item1 < (time - dns.Item4))
                {
                    _dns[host] = Tuple.Create(time, dns.Item2, dns.Item3, dns.Item4);
                    new Task(async () =>
                    {
                        var ips = await Dns.GetHostAddressesAsync(host, cancellationToken);
                        // var ips = await HttpDnsIPAsync(host, dns.Item2, cancellationToken);
                        // var ips = HttpDnsIP(host, out var ttl, dns.Item2);
                        _dns[host] = Tuple.Create(Utility.TimeSpan(), dns.Item2, ips, 600);

                    }).Start();
                }
            }
            switch (dns.Item3.Length)
            {
                case 0:
                case 1:
                    return dns.Item3;
                default:
                    if (dns.Item2.HasFlag(DNSType.IP6) && dns.Item2.HasFlag(DNSType.IP4) == false)
                    {
                        return dns.Item3.Where(r => r.AddressFamily == AddressFamily.InterNetworkV6).ToArray();
                    }
                    return dns.Item3;
            }
        }

        // internal static IPAddress[] DNSToIP(string host)
        // {
        //     if (IPAddress.TryParse(host, out var address))
        //     {
        //         return new IPAddress[1] { address };
        //     }
        //     var time = UMC.Data.Utility.TimeSpan();
        //     if (_dns.TryGetValue(host, out var dns) == false)
        //     {
        //         var hindex = host.IndexOf('.');
        //         while (hindex > 0)
        //         {
        //             if (_dns.TryGetValue("*" + host.Substring(hindex), out dns))
        //             {
        //                 break;
        //             }
        //             hindex = host.IndexOf('.', hindex + 1);

        //         }
        //         if (dns == null)
        //         {
        //             var ips = Dns.GetHostAddresses(host);// HttpDnsIP(host, out var ttl);
        //             dns = Tuple.Create(time, DNSType.Unknown, ips, 600);
        //             _dns[host] = dns;
        //             return ips;

        //         }
        //     }
        //     if (dns.Item3.Length == 0)
        //     {
        //         var ips = HttpDnsIP(host, out var ttl, dns.Item2);
        //         dns = Tuple.Create(time, dns.Item2, ips, ttl);
        //         _dns[host] = dns;
        //     }
        //     else
        //     {

        //         if (dns.Item1 < (time - dns.Item4))
        //         {
        //             _dns[host] = Tuple.Create(time, dns.Item2, dns.Item3, dns.Item4);
        //             new Task(() =>
        //             {
        //                 var ips = HttpDnsIP(host, out var ttl, dns.Item2);
        //                 _dns[host] = Tuple.Create(Utility.TimeSpan(), dns.Item2, ips, ttl);

        //             }).Start();
        //         }
        //     }
        //     switch (dns.Item3.Length)
        //     {
        //         case 0:
        //         case 1:
        //             return dns.Item3;
        //         default:
        //             if (dns.Item2.HasFlag(DNSType.IP6) && dns.Item2.HasFlag(DNSType.IP4) == false)
        //             {
        //                 return dns.Item3.Where(r => r.AddressFamily == AddressFamily.InterNetworkV6).ToArray();
        //             }
        //             return dns.Item3;
        //     }
        // }

        static ConcurrentDictionary<string, System.Tuple<uint, DNSType, IPAddress[], int>> _dns = new ConcurrentDictionary<string, Tuple<uint, DNSType, IPAddress[], int>>();
        static Dictionary<string, int> _KeepAlives = new Dictionary<string, int>();

        /// <summary>
        /// 确认是连接是否可用
        /// </summary>
        /// <returns></returns>
        public abstract bool Active();
        /// <summary>
        /// 发送HTTP头部信息
        /// </summary>
        public abstract void Header(NetHttpRequest request);
        /// <summary>
        /// 发送HTTP正文内容
        /// </summary>
        public abstract void Body(byte[] buffer, int offset, int size);
        /// <summary>
        /// 回收端口
        /// </summary>
        public virtual void Recovery(Mime httpMime)
        {


        }
        protected Socket _client;
        public static async Task<Socket> Connect(String host, int port, int timeout, CancellationToken cancellationToken = default)
        {
            var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
            {
                ReceiveTimeout = timeout,
                SendTimeout = timeout,
                ExclusiveAddressUse = true,
                LingerState = new LingerOption(true, 10),
                NoDelay = true
            };
            var addresses = await NetProxy.DNSToIPAsync(host, cancellationToken);

            Exception exception = null;
            for (var i = 0; i < addresses.Length; i++)
            {
                try
                {
                    await client.ConnectAsync(new IPEndPoint(addresses[i], port), cancellationToken);//, cancellationTokenSource.Token);
                    if (i > 0)
                    {
                        NetProxy.DNS(host, addresses[i].AddressFamily == AddressFamily.InterNetworkV6 ? DNSType.IP6 : DNSType.IP4, addresses.AsSpan(i).ToArray());
                    }
                    return client;
                }
                catch (Exception ex)
                {
                    exception = ex;
                }
            }
            // return null;
            throw exception ?? new Exception(host);
            // this.Error = ex;

        }
        protected virtual async void Connect(String host, int port, int timeout, Action result)
        {

            try
            {

                this._client = await Connect(host, port, timeout);
            }
            catch (Exception ex)
            {
                this.Release();
                this.Error = ex;
                this.IsError = true;
            }
            result();
        }
        /// <summary>
        /// 
        /// </summary>
        public virtual void Dispose()
        {
            // Retry = null;
        }
        //public abstract
        /// <summary>
        /// 准备接收
        /// </summary>
        public abstract void Receive(Mime httpMime);

        public abstract int KeepAliveTime
        {
            get;
        }

        /// <summary>
        /// 准备新连接异步
        /// </summary>
        protected abstract void Create(Uri uri, int timeout, Action<NetProxy> action);
        /// <summary>
        /// 连接池
        /// </summary>
        static ConcurrentDictionary<string, NetPool> _pool = new ConcurrentDictionary<string, NetPool>();

        /// <summary>
        /// 连接池
        /// </summary>
        public static ConcurrentDictionary<string, NetPool> Pool
        {
            get
            {
                return _pool;
            }
        }
        public static void Check()
        {

            var ps = _pool.Values.ToArray();
            foreach (var p in ps)
            {
                if (p.Unwanted.TryPop(out var tcp))
                {
                    if (tcp.Active() == false)
                    {
                        p.Unwanted.Push(tcp);
                    }
                    else
                    {
                        while (p.Unwanted.TryPop(out var _tcp))
                        {
                            _tcp.Dispose();
                        }
                    }
                }
            }
        }
        class NetProxyer : IDisposable
        {
            NetHttpRequest request;
            Action<NetProxy> body;
            Action<Exception> error;
            NetHttpResponse response;
            // byte[] header;
            public NetProxyer(SemaphoreSlim semaphore, NetHttpRequest webRequest, NetHttpResponse response, Action<NetProxy> body, Action<Exception> error)
            {
                this.request = webRequest;
                this.body = body;
                this.error = error;
                this.response = response;
                this.semaphore = semaphore;
            }

            SemaphoreSlim semaphore;
            public void Callback()
            {
                semaphore?.Wait();
                if (request.Proxy != null)
                {
                    switch (request.Address.Scheme)
                    {
                        default:
                            new Socks5Http(request.Proxy) { Semaphore = semaphore }.Create(request.Address, request.Timeout, Callback);
                            break;
                        case "https":
                            new Socks5Https(request.Proxy) { Semaphore = semaphore }.Create(request.Address, request.Timeout, Callback);
                            break;
                        case "unix":
                            new Unix() { Semaphore = semaphore }.Create(request.Address, request.Timeout, Callback);
                            break;
                    }
                }
                else
                {
                    switch (request.Address.Scheme)
                    {
                        default:
                            new Http() { Semaphore = semaphore }.Create(request.Address, request.Timeout, Callback);
                            break;
                        case "https":
                            new Https() { Semaphore = semaphore }.Create(request.Address, request.Timeout, Callback);
                            break;
                        case "unix":
                            new Unix() { Semaphore = semaphore }.Create(request.Address, request.Timeout, Callback);
                            break;
                    }
                }
            }
            void Error(NetProxy proxy)
            {
                this.errTimes++;
                if (this.request.ContentLength <= 0)
                {
                    if (proxy.KeepAliveTime > 1)
                    {
                        this.Callback();
                    }
                    else if (this.errTimes < 2)
                    {
                        this.Callback();
                    }
                    else
                    {
                        var pool = Pool.GetOrAdd(request.Address.Authority, k => new NetPool(request.Address.Authority));
                        pool.BurstError++;
                        pool.ErrorTime = Utility.TimeSpan();

                        error(proxy.Error);
                    }

                }
                else
                {

                    error(proxy.Error);
                }
            }
            int errTimes;
            public void Callback(NetProxy proxy)
            {

                proxy.Receive(this.response);
                if (proxy.IsError == false)
                {

                    proxy.Header(request);
                    proxy.ErrorCall = this.Error;

                    if (proxy.IsError)
                    {
                        if (proxy.KeepAliveTime > 1)
                        {
                            this.errTimes++;
                            this.Callback();
                        }
                        else if (this.errTimes < 2)
                        {
                            this.Callback();
                        }
                        else
                        {
                            var pool = Pool.GetOrAdd(request.Address.Authority, k => new NetPool(request.Address.Authority));
                            pool.BurstError++;
                            pool.ErrorTime = Utility.TimeSpan();
                            error(proxy.Error);

                        }
                        return;
                    }
                    else
                    {
                        this.body(proxy);
                    }
                }
                else
                {
                    var pool = Pool.GetOrAdd(request.Address.Authority, k => new NetPool(request.Address.Authority));
                    pool.BurstError++;
                    pool.ErrorTime = Utility.TimeSpan();
                    error(proxy.Error);
                }
            }

            public void Dispose()
            {
                this.request = null;
                this.body = null;
                this.error = null;
                this.response = null;

                GC.SuppressFinalize(this);
            }
        }
        public static void Instance(NetHttpRequest webRequest, NetHttpResponse response, Action<NetProxy> body, Action<Exception> error)
        {
            NetPool pool;
            if (Pool.TryGetValue(webRequest.Address.Authority, out pool))
            {


                if (pool.BurstError > 10)
                {
                    if (pool.ErrorTime > UMC.Data.Utility.TimeSpan() - 10)
                    {
                        error(new System.Net.WebException($"{webRequest.Address.Authority}连续异常超过10次,请10s后再试"));
                        return;
                    }
                }
                else
                {
                    if (pool.Unwanted.TryPop(out var tc))
                    {
                        if (tc.Active())
                        {
                            tc.Semaphore = pool.Semaphore;
                            pool.Semaphore?.Wait();
                            new NetProxyer(pool.Semaphore, webRequest, response, body, error).Callback(tc);
                            return;
                        }
                        else
                        {
                            while (pool.Unwanted.TryPop(out tc))
                            {
                                tc.Dispose();
                            }
                        }
                    }
                }
            }

            new NetProxyer(pool?.Semaphore, webRequest, response, body, error).Callback();



        }

        class HttpSocketAsyncEventArgs : SocketAsyncEventArgs
        {
            public Mime Mime { get; set; }
        }

        class Http : NetProxy, IDisposable
        {
            public Http()
            {
                this.writeData = this.Write;
            }
            public override void Log(NetWriteData writeData)
            {
                if (writeData != null)
                {
                    this.writeData = new NetWriteData((buffer, offset, size) =>
                    {
                        Write(buffer, offset, size);
                        writeData.Invoke(buffer, offset, size);
                    });
                }
                else
                {
                    this.writeData = this.Write;
                }
            }
            HttpSocketAsyncEventArgs httpSocketAsyncEventArgs = new HttpSocketAsyncEventArgs();
            protected void Receive()
            {
                if (this.IsError == false)
                {
                    httpSocketAsyncEventArgs.SetBuffer(_buffers);
                    httpSocketAsyncEventArgs.Completed += Receive;
                }
            }
            NetWriteData writeData;

            void Write(byte[] buffer, int offset, int size)
            {
                _client.Send(buffer, offset, size, SocketFlags.None);
            }
            protected override void Create(Uri uri, int timeout, Action<NetProxy> result)
            {
                this.key = uri.Authority;
                this.host = uri.Host;
                this.activeTime = DateTime.Now;
                Connect(this.host, uri.Port, timeout, () =>
                {
                    Receive();
                    result(this);
                });

            }
            public override int KeepAliveTime => _KeepAliveTime;
            protected String key;
            // protected Socket _client;
            public DateTime activeTime;
            int _keepAlive = 60;
            protected String host;
            // int port;

            bool disposed = false;

            public override void Body(byte[] buffer, int offset, int size)
            {

                try
                {
                    if (size > 0 && IsError == false)
                    {
                        this.writeData(buffer, offset, size);//, SocketFlags.None);
                    }
                }
                catch (Exception ex)
                {
                    IsError = true;
                    Error = ex;
                }
            }
            public override void Header(NetHttpRequest request)
            {

                try
                {
                    if (this.IsError == false)
                    {
                        NetHttpResponse.Header(request, this.writeData);
                    }

                }
                catch (Exception ex)
                {
                    IsError = true;
                    Error = ex;
                }
            }

            public override void Recovery(Mime mimeBody)
            {
                if (disposed == false)
                {
                    if (mimeBody.KeepAlive > 0)
                    {
                        this._keepAlive = mimeBody.KeepAlive;
                    }
                    else if (_KeepAlives.TryGetValue(this.key, out this._keepAlive) == false)
                    {
                        this._keepAlive = 60;
                    }
                    if (mimeBody.IsClose || mimeBody.IsHttpFormatError || (mimeBody.KeepAliveMax > 0 && mimeBody.KeepAliveMax == this.KeepAliveTime) || this._keepAlive == 0)
                    {
                        NetPool pool;
                        if (Pool.TryGetValue(this.key, out pool))
                        {
                            pool.BurstError = 0;
                        }

                        this.Dispose();
                    }
                    else
                    {

                        var qu = Pool.GetOrAdd(this.key, k => new NetPool(this.key));
                        qu.BurstError = 0;
                        this.activeTime = DateTime.Now;
                        qu.Unwanted.Push(this);

                    }
                }
            }
            public override void Dispose()
            {
                if (disposed == false)
                {
                    httpSocketAsyncEventArgs.Mime = null;
                    httpSocketAsyncEventArgs.Dispose();
                    disposed = true;

                    try
                    {
                        _client?.Shutdown(SocketShutdown.Both);
                        this._client?.Close();
                        this._client?.Dispose();
                    }
                    catch
                    {

                    }

                    if (_buffers != null)
                        System.Buffers.ArrayPool<byte>.Shared.Return(_buffers);

                    _buffers = null;

                    GC.SuppressFinalize(this);
                }
            }

            ~Http()
            {
                Dispose();
            }
            int _KeepAliveTime = 0;
            public override bool Active()
            {
                if (disposed == false)
                {
                    if (this._keepAlive > 0)
                    {
                        var now = DateTime.Now;
                        if (this.activeTime.AddSeconds(this._keepAlive) < now)
                        {
                            this.Dispose();
                            return false;
                        }
                        if (this._client.Poll(-1, SelectMode.SelectWrite))
                        {
                            return true;
                        }
                        else
                        {

                            this.Dispose();
                        }
                        return false;
                    }
                    else
                    {
                        var now = DateTime.Now;
                        if (this.activeTime.AddSeconds(60) < now)
                        {
                            this.Dispose();
                            return false;
                        }

                        if (this._client.Poll(-1, SelectMode.SelectWrite))
                        {
                            return true;
                        }
                        else
                        {

                            this.Dispose();
                        }
                        return true;
                    }
                }
                return false;


            }

            public override void Receive(Mime mimeBody)
            {

                _KeepAliveTime++;
                if (IsError == false)
                {
                    httpSocketAsyncEventArgs.Mime = mimeBody;
                    this._client.ReceiveAsync(httpSocketAsyncEventArgs);
                }


            }
            private void Receive(object sender, SocketAsyncEventArgs e)
            {
                switch (e.LastOperation)
                {
                    case SocketAsyncOperation.Receive:
                        ProcessReceive(e);
                        break;
                    default:
                        this.Dispose();
                        break;
                }
            }
            private bool processReceive()
            {
                var m = this.httpSocketAsyncEventArgs.Mime;
                if (this.httpSocketAsyncEventArgs.SocketError == SocketError.Success)
                {
                    if (this.httpSocketAsyncEventArgs.BytesTransferred > 0)
                    {

                        if (m == null)
                        {
                            this.Dispose();
                            return false;
                        }
                        try
                        {
                            this.activeTime = DateTime.Now;
                            var size = this.httpSocketAsyncEventArgs.Offset + this.httpSocketAsyncEventArgs.BytesTransferred;
                            var data = _buffers;
                            int start = m.Receive(_buffers, 0, size);

                            if (start > 0)
                            {
                                Array.Copy(data, size - start, data, 0, start);
                                this.httpSocketAsyncEventArgs.SetBuffer(data, start, data.Length - start);
                            }
                            else
                            {
                                this.httpSocketAsyncEventArgs.SetBuffer(data);
                            }

                        }
                        catch (Exception ex)
                        {

                            this.Release();
                            this.Dispose();
                            this.IsError = true;
                            this.Error = ex;

                            var action = this.ErrorCall;
                            this.ErrorCall = null;

                            if (action != null)
                            {
                                action(this);
                            }
                            return false;
                        }

                        if (m.IsMimeFinish)
                        {
                            this.Release();
                            this.Recovery(m);
                            this.httpSocketAsyncEventArgs.Mime = null;
                            return false;
                        }
                        else if (m.IsHttpFormatError)
                        {
                            this.Release();
                            this.Dispose();

                            return false;
                        }
                        else
                        {
                            return true;
                        }
                    }
                    return false;

                }
                else
                {


                    this.Release();

                    this.IsError = true;
                    this.Error = new SocketException((int)this.httpSocketAsyncEventArgs.SocketError);

                    var action = this.ErrorCall;
                    this.ErrorCall = null;

                    if (action != null)
                    {
                        action(this);
                    }

                    return false;

                }
            }
            private void ProcessReceive(SocketAsyncEventArgs e)
            {
                if (processReceive())
                {
                    try
                    {
                        while (disposed == false && !this._client.ReceiveAsync(e))
                        {
                            if (!processReceive())
                            {
                                break;
                            }

                        }
                    }
                    catch { }
                }
            }
            byte[] _buffers = System.Buffers.ArrayPool<byte>.Shared.Rent(0x200);


        }

        class Socks5Http : Http
        {
            Socks5 socks5;
            public Socks5Http(Socks5 socks5)
            {
                this.socks5 = socks5;
            }
            public override void Recovery(Mime mimeBody)
            {
                // base.Recovery(mimeBody);
                this.Dispose();
            }
            // override 
            protected override async void Connect(string host, int port, int timeout, Action result)
            {

                try
                {
                    this._client = await this.socks5.ConnectAsync(new StringValue(host.UTF8()), port, timeout);
                    if (timeout > 0)
                    {
                        _client.ReceiveTimeout = timeout;
                    }

                }
                catch (Exception ex)
                {
                    this.IsError = true;
                    this.Error = ex;

                    this.Release();
                }
                result();
            }
        }
        class Socks5Https : Https
        {
            Socks5 socks5;
            public Socks5Https(Socks5 socks5)
            {
                this.socks5 = socks5;
            }
            public override void Recovery(Mime m)
            {
                this.Dispose();
            }
            // override 
            protected override async void Connect(string host, int port, int timeout, Action result)
            {

                try
                {
                    this._client = await this.socks5.ConnectAsync(new StringValue(host.UTF8()), port, timeout);
                    if (timeout > 0)
                    {
                        _client.ReceiveTimeout = timeout;
                    }

                }
                catch (Exception ex)
                {
                    this.IsError = true;
                    this.Error = ex;

                    this.Release();
                }
                result();
            }
        }

        class Unix : Http
        {
            protected override void Connect(string host, int post, int timeout, Action result)
            {

                this._client = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified);

                if (timeout > 0)
                {
                    _client.ReceiveTimeout = timeout;
                }
                try
                {
                    this._client.Connect(new UnixDomainSocketEndPoint(host));

                }
                catch (Exception ex)
                {
                    this.IsError = true;
                    this.Error = ex;

                    this.Release();
                }
                result();
            }
            protected override void Create(Uri uri, int timeout, Action<NetProxy> result)
            {
                this.key = uri.LocalPath.GetHashCode().ToString();
                this.host = uri.LocalPath;
                this.activeTime = DateTime.Now;
                Connect(this.host, 0, timeout, () =>
                {
                    Receive();
                    result(this);
                });

            }

        }

        /// <summary>
        /// 对NetProxy默认实现
        /// </summary>
        class Https : NetProxy, IDisposable
        {
            public override int KeepAliveTime => _KeepAliveTime;
            protected override void Create(Uri uri, int timeout, Action<NetProxy> result)
            {

                var tcp = this;
                this.key = uri.Authority;
                this.host = uri.Host;
                this.activeTime = DateTime.Now;
                Connect(this.host, uri.Port, timeout, async () =>
               {
                   try
                   {
                       if (this.IsError == false)
                       {
                           SslStream ssl = new SslStream(new NetworkStream(this._client, true), false, (a, b, c, d) => true);
                           await ssl.AuthenticateAsClientAsync(this.host, new X509CertificateCollection(), SslProtocols.None, false);
                           this._stream = ssl;
                       }
                   }
                   catch (Exception ex)
                   {
                       this.Error = ex;
                       this.IsError = true;
                       this.Release();
                   }
                   // Receive();
                   result(this);
               });
            }
            String key;
            Stream _stream;
            // Socket _client;
            public DateTime activeTime;
            int _keepAlive = 60;
            String host;
            // int port;
            NetWriteData writeData;
            public Https()
            {
                this.writeData = this.Write;
            }
            void Write(byte[] buffer, int offset, int size)
            {
                _stream.Write(buffer, offset, size);
            }
            public override void Log(NetWriteData writeData)
            {
                if (writeData != null)
                {
                    this.writeData = new NetWriteData((buffer, offset, size) =>
                    {
                        Write(buffer, offset, size);
                        writeData.Invoke(buffer, offset, size);
                    });
                }
                else
                {
                    this.writeData = this.Write;
                }
            }

            bool disposed = false;
            public override void Body(byte[] buffer, int offset, int size)
            {
                try
                {
                    if (size > 0 && IsError == false)
                    {
                        this.writeData(buffer, offset, size);
                    }
                }
                catch (Exception ex)
                {
                    IsError = true;
                    Error = ex;
                }
            }


            public override void Header(NetHttpRequest request)
            {
                try
                {
                    if (this.IsError == false)
                    {
                        NetHttpResponse.Header(request, this.writeData);
                    }
                }
                catch (Exception ex)
                {
                    IsError = true;
                    Error = ex;
                }
            }

            public override void Recovery(Mime m)
            {
                if (disposed == false)
                {
                    if (m.KeepAlive > 0)
                    {
                        this._keepAlive = m.KeepAlive;
                    }
                    else if (_KeepAlives.TryGetValue(this.key, out this._keepAlive) == false)
                    {
                        this._keepAlive = 60;
                    }
                    if (m.IsClose || m.IsHttpFormatError || (m.KeepAliveMax > 0 && m.KeepAliveMax == this._KeepAliveTime) || this._keepAlive == 0)
                    {
                        NetPool pool;
                        if (Pool.TryGetValue(this.key, out pool))
                        {
                            pool.BurstError = 0;
                        }

                        this.Dispose();
                    }
                    else
                    {

                        var qu = Pool.GetOrAdd(this.key, k => new NetPool(this.key));
                        qu.BurstError = 0;
                        qu.Unwanted.Push(this);

                    }
                }
            }
            public override void Dispose()
            {
                if (disposed == false)
                {

                    disposed = true;
                    try
                    {
                        this._client?.Close();
                        this._client?.Dispose();
                    }
                    catch
                    {

                    }
                    _stream?.Close();


                    GC.SuppressFinalize(this);
                }
            }

            ~Https()
            {
                Dispose();
            }
            int _KeepAliveTime = 0;
            public override bool Active()
            {
                if (disposed == false)
                {
                    if (this._keepAlive > 0)
                    {
                        var now = DateTime.Now;
                        if (this.activeTime.AddSeconds(this._keepAlive) < now)
                        {
                            this.Dispose();
                            return false;
                        }

                        // _maxTime++;

                        if (this._client.Poll(-1, SelectMode.SelectWrite))
                        {
                            return true;
                        }
                        else
                        {

                            this.Dispose();
                        }
                        return false;
                    }
                    else
                    {
                        var now = DateTime.Now;
                        if (this.activeTime.AddSeconds(60) < now)
                        {
                            this.Dispose();
                            return false;
                        }

                        if (this._client.Poll(-1, SelectMode.SelectWrite))
                        {
                            return true;
                        }
                        else
                        {

                            this.Dispose();
                        }
                        return true;
                    }
                }
                return false;


            }

            public override void Receive(Mime mimeBody)
            {
                if (IsError == false)
                {
                    _KeepAliveTime++;
                    Read(mimeBody);
                }

            }

            async void Read(Mime m)
            {
                byte[] buffers = System.Buffers.ArrayPool<byte>.Shared.Rent(0x200);
                try
                {
                    int start = 0, size = buffers.Length;
                    int l;
                    do
                    {
                        try
                        {
                            l = await _stream.ReadAsync(buffers, start, size);
                        }
                        catch (Exception ex)
                        {
                            this.Release();
                            this.Dispose();

                            this.IsError = true;
                            this.Error = ex;
                            var action = this.ErrorCall;
                            this.ErrorCall = null;
                            if (action != null)
                            {
                                action(this);
                            }
                            return;
                        }
                        start += l;
                        if (start > 0)
                        {
                            this.activeTime = DateTime.Now;
                            var len = m.Receive(buffers, 0, start);
                            if (len > 0)
                            {
                                Array.Copy(buffers, start - len, buffers, 0, len);
                                start = len;
                                size = buffers.Length - len;
                            }
                            else
                            {
                                start = 0;
                                size = buffers.Length;
                            }
                            if (m.IsMimeFinish)
                            {
                                this.Release();
                                this.Recovery(m);
                                return;
                            }
                            else if (m.IsHttpFormatError)
                            {
                                this.Release();
                                this.Dispose();
                                return;
                            }

                        }
                    } while (l > 0 && disposed == false);
                    this.IsError = true;
                    this.Release();
                    this.Dispose();
                }
                finally
                {
                    System.Buffers.ArrayPool<byte>.Shared.Return(buffers);
                }
            }
        }
    }
}